﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

namespace CoveSmushbox
{
    public class Smushbox
        : IDisposable
    {
        public string Host { get; set; }

        private int _maximimMessagesToSend = 10;
        /// <summary>
        /// When a message needs to be split up into multiple messages, this sets an upper limit to how many parts we will send.
        /// </summary>
        public int MaximimMessagesToSend
        {
            get
            {
                return _maximimMessagesToSend;
            }
            set 
            {
                _maximimMessagesToSend = value;
            }
        }
        
        private string _userName, _password;

        private WebClient _webClient = new WebClientEx();

        public Smushbox()
        {
            Host = "smushbox";
        }

        public Smushbox(string host)
        {
            Host = host;
        }

        public void Login(string UserName, string Password)
        {
            var request = new Info.LoginRequest() { username = UserName, password = Password };

            var json = JsonConvert.SerializeObject(request);

            var data = _webClient.UploadData("http://" + Host + "/index.php/account/login", System.Text.Encoding.UTF8.GetBytes(json));

            json = System.Text.Encoding.UTF8.GetString(data);

            var response = JsonConvert.DeserializeObject<Info.Response<string>>(json);

            if (!response.success)
            {
                if (response.errors != null && response.errors.Any())
                {
                    throw new System.Security.Authentication.AuthenticationException(String.Join("\r\n", response.errors));
                }
                else
                {
                    throw new System.Security.Authentication.AuthenticationException(response.message);
                }
            }

            _userName = UserName;
            _password = Password;
        }

        private byte[] UploadData(string address)
        {
            return UploadData(address, new byte[] { });
        }

        private byte[] UploadData(string address, byte[] data)
        {
            try
            {
                return _webClient.UploadData(address, data);
            }
            catch (WebException wex)
            {
                if (wex.Status == WebExceptionStatus.ProtocolError)
                {
                    var response = wex.Response as HttpWebResponse;
                    if (response != null && response.StatusCode == HttpStatusCode.Unauthorized)
                    {
                        // The request was unauthorized, session might have expired
                        // Try to login again
                        Login(_userName, _password);

                        // Try the request again
                        return _webClient.UploadData(address, data);
                    }
                }

                // Otherwise throw the error
                throw;
            }
        }

        public void SendMessage(string number, string message, SmsSplitTypes splitType = SmsSplitTypes.ByWord)
        {
            SendMessage(number, message, TimeSpan.Zero, splitType);
        }

        /// <summary>
        /// Sends a SMS message.
        /// </summary>
        /// <param name="number">The telephone number to send the message to.</param>
        /// <param name="message">The message to send.</param>
        /// <param name="delay">The time to wait before sending the message.</param>
        /// <param name="splitType">If the message is > 160 characters, this determines whether to send multiple messages and how to split them.</param>
        /// <param name="maximumMessages">If multiple messages are being sent, sets a limit on the maximum messages to send.</param>
        public void SendMessage(string number, string message, TimeSpan delay, SmsSplitTypes splitType)
        {
            var messages = message.SmsSplit(splitType);

            foreach (var currentMessage in messages.Take(MaximimMessagesToSend))
            {
                var request = new Info.SendMessageRequest() { number = number, message = currentMessage.SanitizeForSms(), delay_for = (int)delay.TotalSeconds };

                var json = JsonConvert.SerializeObject(request);

                var data = UploadData("http://" + Host + "/messagelist/send", System.Text.Encoding.UTF8.GetBytes(json));

                json = System.Text.Encoding.UTF8.GetString(data);

                var response = JsonConvert.DeserializeObject<Info.Response<string>>(json);

                if (!response.success)
                {
                    if (response.errors != null && response.errors.Any())
                    {
                        throw new Exception(String.Join("\r\n", response.errors));
                    }
                    else
                    {
                        throw new Exception(response.message);
                    }
                }
            }
        }

        public IEnumerable<Info.Message> GetIncomingMessages()
        {
            var data = UploadData("http://" + Host + "/messagelist/list/incoming/all");

            var json = System.Text.Encoding.UTF8.GetString(data);

            var response = JsonConvert.DeserializeObject<Info.Response<Info.Message[]>>(json);

            if (!response.success)
            {
                if (response.errors != null && response.errors.Any())
                {
                    throw new Exception(String.Join("\r\n", response.errors));
                }
                else
                {
                    throw new Exception();
                }
            }

            return response.message;
        }

        public IEnumerable<Info.Message> GetOutgoingMessages()
        {
            var data = UploadData("http://" + Host + "/messagelist/list/outgoing/all");

            var json = System.Text.Encoding.UTF8.GetString(data);

            var response = JsonConvert.DeserializeObject<Info.Response<Info.Message[]>>(json);

            if (!response.success)
            {
                if (response.errors != null && response.errors.Any())
                {
                    throw new Exception(String.Join("\r\n", response.errors));
                }
                else
                {
                    throw new Exception();
                }
            }

            return response.message;
        }

        public void DeleteIncomingMessage(long messageId)
        {
            var data = UploadData("http://" + Host + "/messagelist/delete/incoming/" + messageId.ToString());

            var json = System.Text.Encoding.UTF8.GetString(data);

            var response = JsonConvert.DeserializeObject<Info.Response<string>>(json);

            if (!response.success)
            {
                if (response.errors != null && response.errors.Any())
                {
                    throw new Exception(String.Join("\r\n", response.errors));
                }
                else
                {
                    throw new Exception(response.message);
                }
            }
        }

        public void DeleteOutgoingMessage(long messageId)
        {
            var data = UploadData("http://" + Host + "/messagelist/delete/outgoing/" + messageId.ToString());

            var json = System.Text.Encoding.UTF8.GetString(data);

            var response = JsonConvert.DeserializeObject<Info.Response<string>>(json);

            if (!response.success)
            {
                if (response.errors != null && response.errors.Any())
                {
                    throw new Exception(String.Join("\r\n", response.errors));
                }
                else
                {
                    throw new Exception(response.message);
                }
            }
        }

        private bool _disposed;

        private void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            if (disposing)
            {
                _webClient.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}
